home *** CD-ROM | disk | FTP | other *** search
/ GameStar 2004 April / Gamestar_61_2004-04_dvdb.iso / DVDStar / Editace / hltp.exe / {app} / Applications / QuArK / plugins / mapcsg.py < prev    next >
Text File  |  2004-01-05  |  15KB  |  381 lines

  1. """   QuArK  -  Quake Army Knife
  2.  
  3. Implementation of the Brush Subtraction commands
  4. """
  5. #
  6. # Copyright (C) 1996-99 Armin Rigo
  7. # THIS FILE IS PROTECTED BY THE GNU GENERAL PUBLIC LICENCE
  8. # FOUND IN FILE "COPYING.TXT"
  9. #
  10.  
  11. #$Header: /cvsroot/quark/runtime/plugins/mapcsg.py,v 1.6 2003/03/24 08:57:15 cdunde Exp $
  12.  
  13.  
  14.  
  15. Info = {
  16.    "plug-in":       "CSG Brush Subtraction",
  17.    "desc":          "Various polyhedron subtraction commands.",
  18.    "date":          "31 oct 98",
  19.    "author":        "Armin Rigo",
  20.    "author e-mail": "arigo@planetquake.com",
  21.    "quark":         "Version 5.1" }
  22.  
  23.  
  24. import quarkx
  25. from quarkpy.maputils import *
  26. import quarkpy.qmenu
  27. import quarkpy.mapcommands
  28. import quarkpy.mapentities
  29.  
  30.  
  31. def CSGinfo():
  32.     quarkx.msgbox("To subtract a polyhedron\n - from the map : first select the polyhedron;\n - from another : first select them both.\n\nYou can use a group of polyhedrons as subtracter instead of a single polyhedron.\n\nSee also the help (F1) of the Brush substraction menu command.",
  33.       MT_INFORMATION, MB_OK)
  34.  
  35.  
  36. def CSG1click(m):
  37.     editor = mapeditor()
  38.     if editor is None: return
  39.     list = editor.visualselection()
  40.     subtracter = editor.layout.explorer.focussel
  41.     if subtracter is None:
  42.         CSGinfo()
  43.         return
  44.     sublist = subtracter.findallsubitems("", ":p")  # find polyhedrons
  45.     if not len(sublist):
  46.         CSGinfo()
  47.         return
  48.     for sel in (list, [editor.Root]):
  49.         plist = []
  50.         for p in sel:
  51.             plist = plist + p.findallsubitems("", ":p")
  52.         for p in sublist:
  53.             try:
  54.                 plist.remove(p)
  55.             except:
  56.                 pass
  57.         if plist:
  58.             break
  59.     if not plist:
  60.         CSGinfo()
  61.         return
  62.  
  63.     CSG(editor, plist, sublist, "polyhedron subtraction")
  64.  
  65.  
  66.  
  67. def CSG(editor, plist, sublist, undomsg, undo=None):
  68.  
  69.     # We compute the subtraction operation
  70.  
  71.     source = plist
  72.     progr = quarkx.progressbar(508, len(sublist))
  73.     try:
  74.         for p in sublist:
  75.             plist = p.subtractfrom(plist)
  76.             progr.progress()
  77.     finally:
  78.         progr.close()
  79.  
  80.     # We add the pieces of broken polyhedrons into the map
  81.     if undo is None:
  82.         undo = quarkx.action()
  83.     for p in plist:
  84.         if p.pieceof is not None:    # p comes from a polyhedron in 'source' that was broken into pieces
  85.             undo.put(p.pieceof.parent, p, p.pieceof)
  86.             # we put 'p' into the group that was the parent of the polyhedron
  87.             # whose 'p' is a piece of, and we insert 'p' right before it
  88.             # (it will be removed anyway by the "exchange" command below,
  89.             #  so before or after doesn't matter).
  90.  
  91.     # If you feel like, you can add code so that when a single polyhedron is broken into
  92.     # several pieces, the pieces are put into a new group. You can also change the name
  93.     # of the pieces (by default, they all have the name of the original polyhedron).
  94.  
  95.     # We remove the broken polyhedrons
  96.     for p in source:
  97.         if not (p in plist):     # original polyhedron was broken into pieces
  98.             undo.exchange(p, None)   # remove it from the map
  99.  
  100.     editor.ok(undo, undomsg)
  101.  
  102. def polyintersects(p1, p2):
  103.     p = quarkx.newobj("test:p")
  104.     for sp in (p1, p2):
  105.         for face in sp.faces:
  106.             p.appenditem(face.copy())
  107.     return len(p.faces)
  108.  
  109. mat_shrink=quarkx.matrix((0.99,0,0), (0,0.99,0), (0,0,0.99))
  110.  
  111. #
  112. # CSG for non-map stuff
  113. #
  114. def CSGlist(plist, sublist):
  115.  
  116.     # We compute the subtraction operation
  117.  
  118.     source = plist
  119.     progr = quarkx.progressbar(508, len(sublist))
  120.     try:
  121.         for p in sublist:
  122.             tp = p.copy()
  123.             tp.linear(tp.origin, mat_shrink)
  124.  #           tp.shortname="shrunk"
  125.  #           undo = quarkx.action()
  126.  #           undo.put(p.parent,tp,p)
  127.  
  128.             tplist = plist[:]
  129.             ignored = []
  130.             for targ in tplist[:]:
  131.               if not polyintersects(tp,targ):
  132.                 tplist.remove(targ)
  133.                 ignored.append(targ)
  134.             plist = p.subtractfrom(tplist)+ignored
  135.             progr.progress()
  136.     finally:
  137.         progr.close()
  138.  
  139.     # We add the pieces of broken polyhedrons into the map
  140.     for p in plist:
  141.         if p.pieceof is not None:    # p comes from a polyhedron in 'source' that was broken into pieces
  142.             p.pieceof.parent.appenditem(p)
  143. #            undo.put(p.pieceof.parent, p, p.pieceof)
  144.             # we put 'p' into the group that was the parent of the polyhedron
  145.             # whose 'p' is a piece of, and we insert 'p' right before it
  146.             # (it will be removed anyway by the "exchange" command below,
  147.             #  so before or after doesn't matter).
  148.  
  149.     # If you feel like, you can add code so that when a single polyhedron is broken into
  150.     # several pieces, the pieces are put into a new group. You can also change the name
  151.     # of the pieces (by default, they all have the name of the original polyhedron).
  152.  
  153.     # We remove the broken polyhedrons
  154.     for p in source:
  155.         if not (p in plist):     # original polyhedron was broken into pieces
  156. #            undo.exchange(p, None)   # remove it from the map
  157.             p.parent.removeitem(p)
  158.  
  159.  
  160. #DECKER-begin Code by tiglari
  161. def ExtWall1click(m):
  162.     editor = mapeditor()
  163.     if editor is None: return
  164.     plist = []
  165.     for p in editor.visualselection():
  166.         plist = plist + p.findallsubitems("", ":p")  # find selected polyhedrons
  167.     if not len(plist):
  168.         quarkx.msgbox("This command lets you turn polyhedrons into rooms by extruding walls from their faces. It makes in one or several polyhedrons a room with the same shape.\n\nSelect the polyhedron(s) first. Note that wall thickness can be chosen in the Movement Palette configuration box, under 'Inflate/Deflate'.",
  169.           MT_INFORMATION, MB_OK)
  170.         return
  171.     extrudewalls(editor, plist)
  172.  
  173. def extrudewalls(editor, plist, wallwidth=None):
  174.     import quarkpy.qmovepal
  175.     wallwidth, = quarkpy.qmovepal.readmpvalues("WallWidth", SS_MAP)
  176.     if wallwidth > 0:           #DECKER
  177.         wallwidth = -wallwidth  #DECKER
  178.     if wallwidth < 0:           #DECKER
  179.         undo = quarkx.action()
  180.         for p in plist:
  181.           newg = quarkx.newobj(p.shortname+" group:g")
  182.           for f in p.faces:
  183.             walls = f.extrudeprism(p)
  184.             for wall in walls:
  185.               wall.texturename=f.texturename
  186.             inner = f.copy()
  187.             inner.swapsides()
  188.             outer = f.copy()
  189.             n = f.normal
  190.             n = n.normalized
  191.             outer.translate(abs(wallwidth)*n)
  192.             newp = quarkx.newobj("wall:p")
  193.             for face in walls + [inner, outer]:
  194.               newp.appenditem(face)
  195.             newg.appenditem(newp)
  196.           undo.exchange(p, newg)
  197.         editor.ok(undo,"extrude walls")
  198.     else: #DECKER
  199.         quarkx.msgbox("Error! 'Inflate/Deflate' value is 0.", MT_INFORMATION, MB_OK) #DECKER
  200. #DECKER-end
  201.  
  202.  
  203.  
  204. def Hollow1click(m):
  205.     editor = mapeditor()
  206.     if editor is None: return
  207.     plist = []
  208.     for p in editor.visualselection():
  209.         plist = plist + p.findallsubitems("", ":p")  # find selected polyhedrons
  210.     if not len(plist):
  211.         quarkx.msgbox("This command lets you 'dig' into polyhedrons. It makes in one or several polyhedrons a room with the same shape.\n\nSelect the polyhedron(s) first. Note that wall thickness can be chosen in the Movement Palette configuration box, under 'Inflate/Deflate'.",
  212.           MT_INFORMATION, MB_OK)
  213.         return
  214.  
  215.     import quarkpy.qmovepal
  216.     wallwidth, = quarkpy.qmovepal.readmpvalues("WallWidth", SS_MAP)
  217.  
  218.     if wallwidth <= 0:
  219.  
  220.         sublist = []
  221.         for p in plist:
  222.             new = quarkx.newobj("neg:p")
  223.             for f in p.faces:
  224.                 new.appenditem(f.copy())
  225.             new.inflate(wallwidth)
  226.             if not new.broken:
  227.                 sublist.append(new)
  228.         if not len(sublist):
  229.             if quarkx.msgbox("Not enough room in the polyhedron(s) to make the hole.\n\nYou can set the wall width in the Movement Palette configuration box, under 'Inflate/Deflate'. Do you want to open this box now ?",
  230.               MT_INFORMATION, MB_YES | MB_NO) == MR_YES:
  231.                 quarkpy.qmovepal.ConfigDialog(SS_MAP)
  232.             return
  233.         CSG(editor, plist, sublist, "make hollow")
  234.  
  235.     else:
  236.  
  237.         biglist = []
  238.         undo = quarkx.action()
  239.         for p in plist:
  240.             subitems = p.subitems
  241.             for f in p.faces:
  242.                 if not (f in subitems):
  243.                     quarkx.msgbox("You cannot inflate a polyhedron with a shared face. Select a negative wall width and try again.",
  244.                       MT_INFORMATION, MB_OK)
  245.                     return
  246.             new = p.copy()
  247.             new.inflate(wallwidth)
  248.             if not new.broken:
  249.                 biglist.append(new)
  250.                 undo.exchange(p, new)
  251.         CSG(editor, biglist, plist, "make hollow", undo)
  252.  
  253.  
  254. def Intersect1click(m):
  255.     editor = mapeditor()
  256.     if editor is None: return
  257.     plist = []
  258.     for p in editor.visualselection():
  259.         plist = plist + p.findallsubitems("", ":p")  # find selected polyhedrons
  260.     if len(plist)<=1:
  261.         quarkx.msgbox("To compute the intersection of two or more polyhedrons, select them all, first.",
  262.           MT_INFORMATION, MB_OK)
  263.         return
  264.     new = quarkx.newobj("intersection:p")
  265.     for p in plist:
  266.         for f in p.faces:
  267.             new.appenditem(f.copy())
  268.     if new.broken:
  269.         quarkx.msgbox("The polyhedrons have no valid intersection.",
  270.           MT_INFORMATION, MB_OK)
  271.         return
  272.  
  273.     undo = quarkx.action()
  274.     undo.exchange(plist[0], new)
  275.     for p in plist[1:]:
  276.         undo.exchange(p, None)
  277.     editor.ok(undo, "intersection")
  278.  
  279.  
  280. def FaceSubinfo():
  281.     quarkx.msgbox("This command works like 'Brush subtraction', except that it produces shared faces. This is useful if you want to edit the subtracted polyhedrons later, but can be confusing if you are not used to shared faces.",
  282.       MT_INFORMATION, MB_OK)
  283.  
  284.  
  285. def FaceSub1click(m):
  286.     editor = mapeditor()
  287.     if editor is None: return
  288.     list = editor.visualselection()
  289.     subtracter = editor.layout.explorer.focussel
  290.     if subtracter is None:
  291.         FaceSubinfo()
  292.         return
  293.     sublist = subtracter.findallsubitems("", ":p")  # find polyhedrons
  294.     if not len(sublist):
  295.         FaceSubinfo()
  296.         return
  297.     for p in sublist:
  298.         if p in list:
  299.             list.remove(p)
  300.     if not len(list):
  301.         list = [editor.Root]    # subtract from everything
  302.     plist = []
  303.     for p in list:
  304.         plist = plist + p.findallsubitems("", ":p")
  305.     for p in sublist:
  306.         if p in plist:
  307.             plist.remove(p)
  308.     if not len(plist):
  309.         FaceSubinfo()
  310.         return
  311.  
  312.     undo = quarkx.action()
  313.     for neg in sublist:
  314.         for p in plist:
  315.             if neg.intersects(p):
  316.                 group = quarkx.newobj(p.shortname + ':g')
  317.                 for f in p.subitems:
  318.                     group.appenditem(f.copy())
  319.                 for f in neg.faces:
  320.                     f1 = f.copy()
  321.                     f1.swapsides()
  322.                     test = quarkx.newobj("test:p")
  323.                     for f2 in p.faces:
  324.                         test.appenditem(f2.copy())
  325.                     test.appenditem(f1.copy())
  326.                     if not test.broken:
  327.                         mini = quarkx.newobj(f.shortname + ':p')
  328.                         mini.appenditem(f1)
  329.                         group.appenditem(mini)
  330.                 undo.exchange(p, group)
  331.     editor.ok(undo, "face sharing subtraction")
  332.  
  333.  
  334.  
  335. #--- add the new menu items into the "Commands" menu ---
  336.  
  337. CSG1 = quarkpy.qmenu.item("&Brush subtraction", CSG1click, "|Brush subtraction:\n\nThis function will subtract one brush from another.\n\nFirst select the brush you want the subtraction to occur on.\nNext select the brush that should be subtracted from the first.\nThen you activate this menu item, or press the accellerator key CTRL+B.\n\nSee the infobase for more detail and other ways to use this function.|intro.mapeditor.menu.html#brushsubtraction")
  338.  
  339. FaceSub1 = quarkpy.qmenu.item("&Face Sharing subtraction", FaceSub1click, "|Face Sharing subtraction:\n\nA special version of the previous command, 'Brush subtraction'. The small broken pieces will be designed to share common faces, so that you can still resize the broken polyhedron as a whole without having to resize each piece. This command, however, may produce a result that gets a bit confusing.|intro.mapeditor.menu.html#facesharesubtract")
  340.  
  341. ExtWall1 = quarkpy.qmenu.item("&Extrude walls", ExtWall1click, "|Extrude walls:\n\nThis extrudes walls from the faces, deletes the poly(s).|intro.mapeditor.menu.html#facesharesubtract") #DECKER Code by tiglari
  342.  
  343. Hollow1 = quarkpy.qmenu.item("&Make hollow", Hollow1click, "|Make hollow:\n\nMakes the selected polyhedron or polyhedrons hollow. If several touching polyhedrons are selected, the whole shape they define will be made hollow.\n\nYou can set the wall width by clicking on the button 'change toolbar settings', under 'inflate/deflate by'. A positive value means extruded polyhedrons, a negative value means digged polyhedrons.|intro.mapeditor.menu.html#facesharesubtract")
  344.  
  345. Intersect1 = quarkpy.qmenu.item("&Intersection", Intersect1click, "|Intersection:\n\nComputes the intersection of two or more overlapping polyhedrons.\n\nThis is basically a kind of brush adding function. It will try to create a new polyhedron which occupy the common area of the selected polyhedrons.|intro.mapeditor.menu.html#facesharesubtract")
  346.  
  347. quarkpy.mapcommands.items.append(quarkpy.qmenu.sep)   # separator
  348. quarkpy.mapcommands.items.append(CSG1)
  349. quarkpy.mapcommands.items.append(FaceSub1)
  350. quarkpy.mapcommands.items.append(ExtWall1) #DECKER Code by tiglari
  351. quarkpy.mapcommands.items.append(Hollow1)
  352. quarkpy.mapcommands.items.append(Intersect1)
  353. MapHotKey("Brush Subtraction", CSG1, quarkpy.mapcommands)
  354.  
  355. #--- add a few items to the polyhedrons pop-up menus ---
  356.  
  357. def newmenubegin(o, editor, oldmenubegin = quarkpy.mapentities.PolyhedronType.menubegin.im_func):
  358.     return oldmenubegin(o, editor) + [CSG1, Hollow1, quarkpy.qmenu.sep]
  359.  
  360. quarkpy.mapentities.PolyhedronType.menubegin = newmenubegin
  361.  
  362.  
  363. # ----------- REVISION HISTORY ------------
  364. #
  365. # $Log: mapcsg.py,v $
  366. # Revision 1.6  2003/03/24 08:57:15  cdunde
  367. # To update info and link to infobase
  368. #
  369. # Revision 1.5  2001/03/29 20:57:45  tiglari
  370. # added 2nd subtraction routime, some tests
  371. #
  372. # Revision 1.4  2001/03/20 08:02:16  tiglari
  373. # customizable hot key support
  374. #
  375. # Revision 1.3.4.1  2001/03/11 22:08:15  tiglari
  376. # customizable hot keys
  377. #
  378. # Revision 1.3  2000/06/03 10:25:30  alexander
  379. # added cvs headers
  380. #
  381.